package org.apache.ibatis.common.io;

import org.apache.ibatis.common.logging.Log;
import org.apache.ibatis.common.logging.LogFactory;

import java.io.File;
import java.io.IOException;
import java.lang.reflect.Method;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

/**
 * @Name JBoss6VFS
 * @Description: JBoss 6 VFS [实现] 类
 * 描述信息：该实现类作为 JBoss 6 的代理类，只负责完成将相关操作转给被代理类（JBoss 6）的工作。
 * @Version v1.0.0
 * @Author: lichao
 * @Date: 2024-05-14 17:42:57
 **/
public class JBoss6VFS extends VFS {
    private static final Log log = LogFactory.getLog(JBoss6VFS.class);
    private static Boolean valid;

    static {
        initialize();
    }

    /**
     * 初始化 JBoss6VFS 代理类，检查被代理类是否存在判断自身是否可用。
     */
    protected static synchronized void initialize() {
        if (valid == null) {
            valid = Boolean.TRUE;
            // 检查被代理类是否存在
            VFS.VFS = checkNotNull(getClass("org.jboss.vfs.VFS"));
            VirtualFile.VirtualFile = checkNotNull(getClass("org.jboss.vfs.VirtualFile"));
            // 检查被代理方法是否存在
            VFS.getChild = checkNotNull(getMethod(VFS.VFS, "getChild", URL.class));
            VirtualFile.getChildrenRecursively = checkNotNull(getMethod(VirtualFile.VirtualFile, "getChildrenRecursively"));
            VirtualFile.getPathNameRelativeTo = checkNotNull(getMethod(VirtualFile.VirtualFile, "getPathNameRelativeTo", VirtualFile.VirtualFile));
            // 判断以上所需方法的返回值是否和预期一致
            checkReturnType(VFS.getChild, VirtualFile.VirtualFile);
            checkReturnType(VirtualFile.getChildrenRecursively, List.class);
            checkReturnType(VirtualFile.getPathNameRelativeTo, String.class);
        }
    }

    protected static <T> T checkNotNull(T object) {
        if (object == null) setInvalid();
        return object;
    }

    protected static void checkReturnType(Method method, Class<?> expected) {
        if (method != null && !expected.isAssignableFrom(method.getReturnType())) {
            String msg = "目标方法 [" + method.getClass().getName() + "." + method.getName() + "(..)] 应该返回 {" + expected.getName() + "}，但是却返回了 {" + method.getReturnType().getName() + "}。";
            log.error(msg);
            setInvalid();
        }
    }

    @Override
    public boolean isValid() {
        return valid;
    }

    protected static void setInvalid() {
        if (JBoss6VFS.valid == Boolean.TRUE) {
            log.debug("JBoss 6 VFS API 在当前环境中是不可用的。");
            JBoss6VFS.valid = Boolean.FALSE;
        }
    }

    @Override
    protected List<String> list(URL url, String path) throws IOException {
        VirtualFile directory = VFS.getChild(url);
        if (directory == null) return Collections.emptyList();
        if (!path.endsWith("/")) path += "/";
        List<VirtualFile> children = directory.getChildren();
        List<String> names = new ArrayList<>(children.size());
        for (VirtualFile vf : children) {
            names.add(path + vf.getPathNameRelativeTo(directory));
        }
        return names;
    }

    static class VirtualFile {
        static Class<?> VirtualFile;
        static Method getPathNameRelativeTo, getChildrenRecursively;
        Object virtualFile;

        public VirtualFile(Object virtualFile) {
            this.virtualFile = virtualFile;
        }

        String getPathNameRelativeTo(VirtualFile parent) {
            try {
                return invoke(getPathNameRelativeTo, virtualFile, parent.virtualFile);
            } catch (IOException e) {
                String msg = "这是不应该可能发生的，VirtualFile.getPathNameRelativeTo() 抛出了 IOException 异常。";
                log.error(msg);
                return null;
            }
        }

        List<VirtualFile> getChildren() throws IOException {
            List<?> objects = invoke(getChildrenRecursively, virtualFile);
            List<VirtualFile> children = new ArrayList<>(objects.size());
            for (Object object : objects) {
                children.add(new VirtualFile(object));
            }
            return children;
        }
    }

    static class VFS {
        static Class<?> VFS;
        static Method getChild;

        private VFS() {
        }

        static VirtualFile getChild(URL url) throws IOException {
            Object o = invoke(getChild, VFS, url);
            return o == null ? null : new VirtualFile(o);
        }
    }

}

